package com.easy.frame.servers.sys.service.impl.menu;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easy.frame.base.api.pojo.PageDTO;
import com.easy.frame.base.api.pojo.PageVO;
import com.easy.frame.base.api.util.CollectionUtils;
import com.easy.frame.base.basic.ServiceResponse;
import com.easy.frame.base.basic.constant.DbConstants;
import com.easy.frame.base.basic.constant.MenuConstants;
import com.easy.frame.base.basic.enums.ResponseCodeEnum;
import com.easy.frame.base.basic.exception.ServiceException;
import com.easy.frame.base.basic.pojo.vo.BaseTreeVO;
import com.easy.frame.base.basic.pojo.vo.MenuContentVO;
import com.easy.frame.base.basic.pojo.vo.MenuContentVoList;
import com.easy.frame.base.basic.util.AuthUtils;
import com.easy.frame.base.service.mp.page.PageProxy;
import com.easy.frame.base.service.tree.TreeHandler;
import com.easy.frame.servers.sys.dto.SysMenuPageDTO;
import com.easy.frame.servers.sys.entity.*;
import com.easy.frame.servers.sys.mapper.menu.SysMenuMapper;
import com.easy.frame.servers.sys.mapper.process.SysSettingMapper;
import com.easy.frame.servers.sys.mapper.role.SysRoleMapper;
import com.easy.frame.servers.sys.mapper.staff.SysStaffMapper;
import com.easy.frame.servers.sys.mapper.staff.SysStaffRoleMapper;
import com.easy.frame.servers.sys.mapper.tenant.SysTenantMenuMapper;
import com.easy.frame.servers.sys.mapping.SysMenuMapping;
import com.easy.frame.servers.sys.service.menu.SysMenuService;
import com.easy.frame.servers.sys.service.role.SysRoleMenuService;
import com.easy.frame.servers.sys.service.staff.SysStaffMenuService;
import com.easy.frame.servers.sys.service.staff.SysStaffRoleService;
import com.easy.frame.servers.sys.service.tenant.SysTenantMenuService;
import com.easy.frame.servers.sys.vo.SysMenuListVO;
import com.easy.frame.servers.sys.vo.SysMenuPageVO;
import com.easy.frame.servers.sys.vo.SysMenuTreeVO;
import org.mapstruct.factory.Mappers;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 系统菜单表 服务实现类
 *
 * @author zzf
 * @since 2020-12-14
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Resource
    SysMenuMapper sysMenuMapper;

    @Resource
    SysSettingMapper sysSettingMapper;

    @Resource
    SysStaffRoleMapper staffRoleMapper;

    @Resource
    SysTenantMenuService tenantMenuService;

    @Resource
    SysRoleMenuService roleMenuService;

    @Resource
    SysRoleMapper roleMapper;

    @Resource
    SysStaffMapper staffMapper;

    @Resource
    SysTenantMenuMapper sysTenantMenuMapper;

    @Resource
    SysStaffMenuService staffMenuService;

    @Resource
    SysStaffRoleService staffRoleService;

    SysMenuMapping menuMapping = Mappers.getMapper(SysMenuMapping.class);

    @Override
    public PageVO<SysMenuPageVO> selectForPage(PageDTO<SysMenuPageDTO> param) {
        PageVO<SysMenuPageVO> proxy = PageProxy.proxy(baseMapper::selectForPage, param);
        List<SysMenuPageVO> records = proxy.getRecords();
        ServiceResponse<List<SysMenuPageVO>> success = new ServiceResponse<>(records, ResponseCodeEnum.SUCCESS);
        proxy.setRecords(success.getData());
        return proxy;
    }

    @Override
    public SysMenu selectById(Long id) {
        return baseMapper.selectById(id);
    }

    @Override
    public int countById(Long id) {
        return sysMenuMapper.selectCount(
                new LambdaQueryWrapper<SysMenu>()
                        .eq(SysMenu::getId, id)
                        .eq(SysMenu::getDeleted, DbConstants.DEFAULT_NO)
        );
    }

    @Override
    public int updateDeleted(Long id) {
        SysMenu sysMenu = new SysMenu();
        sysMenu.setId(id);
        sysMenu.setDeleted(DbConstants.DEFAULT_YES);
        return sysMenuMapper.updateById(sysMenu);
    }

    @Override
    public void authTenant(Long tenantId, Map<Long, List<String>> menuId2OptionListMap) {
        authTenantAsync(tenantId, menuId2OptionListMap, AuthUtils.getCurrentUserId());
    }

    @Override
    public void authRole(Long roleId, Map<Long, List<String>> menuId2OptionListMap, boolean isSyncChangeStaff) {
        checkRoleValid(menuId2OptionListMap);
        authRoleAsync(Collections.singletonList(roleId), menuId2OptionListMap, isSyncChangeStaff, AuthUtils.getCurrentUserId());
    }

    /**
     * 判断所授权限是否合法
     * <p>
     * 一般情况下只能授权 该租户拥有的菜单权限
     * 该操作是为了防止手动修改接口参数传入租户不存在的菜单权限
     * 校验逻辑：
     * 1.现根据token中的租户Id查出该租户下的所有租户菜单(该操作是查出该租户所有的菜单)
     * 2.租户菜单转成map(菜单ID，操作权限)
     * 3.遍历前端参数的map，去对比当前租户菜单表中是否存在该菜单ID
     * 简单的来说就是：在添加角色菜单之前先对比下该用户是否有对应租户菜单的权限（那么这样的话是不是要先添加租户菜单，之后再添加角色菜单呢）
     *
     * @param menuId2OptionListMap 授予指定角色的菜单权限
     */
    private void checkRoleValid(Map<Long, List<String>> menuId2OptionListMap) {
        //Integer tenant = AuthUtil.getCurrentUserTenant();
        List<SysTenantMenu> tenantMenuList = tenantMenuService.list(new LambdaQueryWrapper<SysTenantMenu>()
                .eq(SysTenantMenu::getTenantId, AuthUtils.getCurrentUserTenant())
                .select(SysTenantMenu::getMenuId, SysTenantMenu::getOperations)
        );
        if (ObjectUtil.isEmpty(tenantMenuList)) {
            throw new ServiceException("租户菜单权限异常，请联系管理员！");
        }
        //转成map利于判断
        Map<Long, List<String>> hadMenuIdOptionListMap = tenantMenuList.stream().collect(
                Collectors.toMap(
                        SysTenantMenu::getMenuId,
                        SysTenantMenu::getOperations
                )
        );
        //遍历判断
        menuId2OptionListMap.forEach((menuId, optionList) -> {
            List<String> list = new ArrayList<>();
            if (!StringUtils.isEmpty(optionList)) {
                setEntity(list, optionList);
            }
            List<String> hadOptionList = hadMenuIdOptionListMap.get(menuId);
            if (ObjectUtil.isNotEmpty(hadOptionList)) {
                if (hadOptionList.containsAll(list)) {
                    return;
                }
            }
            throw new ServiceException("该租户" + AuthUtils.getCurrentUserTenant() + "没有对应的菜单" + menuId + "权限!");
        });


    }

    @Override
    public void authStaff(Long staffId, Map<Long, List<String>> menuId2OptionListMap) {
        authStaffAsync(Collections.singletonList(staffId), menuId2OptionListMap, AuthUtils.getCurrentUserId());
    }

    @Async
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void authTenantAsync(Long tenantId, Map<Long, List<String>> menuId2OptionListMap, Long currentUserId) {
        //直接删除以前的
        tenantMenuService.remove(new LambdaQueryWrapper<SysTenantMenu>()
                .eq(SysTenantMenu::getTenantId, tenantId)
        );
        //重新添加
        SysTenantMenu tenantMenu = new SysTenantMenu();
        tenantMenu.setCreateBy(currentUserId);
        tenantMenu.setTenantId(tenantId);
        menuId2OptionListMap.forEach((menuId, optionList) -> {
            tenantMenu.setMenuId(menuId);
            List<String> list = new ArrayList<>();
            setEntity(list, optionList);
            tenantMenu.setOperations(list);
            tenantMenuService.save(tenantMenu);
        });
    }

    @Async
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void authRoleAsync(List<Long> roleIdList, Map<Long, List<String>> menuId2OptionListMap, boolean isSyncChangeStaff, Long currentUserId) {
        //先删除所有的(这样达到去重的效果)
        roleMenuService.remove(new LambdaQueryWrapper<SysRoleMenu>()
                .in(SysRoleMenu::getRoleId, roleIdList)
        );
        //再重新添加
        SysRoleMenu roleMenu = new SysRoleMenu();
        roleMenu.setCreateBy(currentUserId);
        roleIdList.forEach(roleId -> {
            roleMenu.setRoleId(roleId);
            menuId2OptionListMap.forEach((menuId, optionList) -> {
                roleMenu.setMenuId(menuId);
                List<String> list = new ArrayList<>();
                setEntity(list, optionList);
                roleMenu.setOperations(list);
                roleMenuService.save(roleMenu);
            });
        });

        //同步修改用户的
        if (isSyncChangeStaff) {
            //查询影响到的用户
            SysStaffMenu staffMenu = new SysStaffMenu();
            staffMenu.setCreateBy(currentUserId);
            List<SysStaffRole> staffRoles = staffRoleMapper.selectListByRoleId(roleIdList);
            if (staffRoles.size() == 0) {
                return;
            }
            List<Long> staffIdList = staffRoles.stream().distinct().map(SysStaffRole::getStaffId).collect(Collectors.toList());

            //先删
            staffMenuService.remove(new LambdaQueryWrapper<SysStaffMenu>()
                    .in(SysStaffMenu::getStaffId, staffIdList)
            );

            //再重新加
            staffIdList.forEach(staffId -> {
                List<SysStaffRole> lastStaffRoleList = staffRoleService.list(new LambdaQueryWrapper<SysStaffRole>()
                        .eq(SysStaffRole::getStaffId, staffId)
                        .select(SysStaffRole::getRoleId)
                );
                List<Long> tempRoleIdList = lastStaffRoleList.stream().map(SysStaffRole::getRoleId).collect(Collectors.toList());
                roleMenuService.list(
                        new LambdaQueryWrapper<SysRoleMenu>()
                                .in(SysRoleMenu::getRoleId, tempRoleIdList)
                                .select(SysRoleMenu::getMenuId, SysRoleMenu::getOperations)
                ).stream().collect(
                        //查询出来后将list转成map
                        Collectors.toMap(
                                //key
                                SysRoleMenu::getMenuId,
                                //value
                                SysRoleMenu::getOperations,
                                //value聚合时的操作
                                CollectionUtils::unionDistinct
                        )
                        //遍历map
                ).forEach((menuId, optionList) -> {
                    staffMenu.setStaffId(staffId);
                    staffMenu.setMenuId(menuId);
                    staffMenu.setOperations(optionList);
                    staffMenuService.save(staffMenu);
                });
            });
        }
    }

    @Async
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void authStaffAsync(List<Long> staffIdList, Map<Long, List<String>> menuId2OptionListMap, Long currentUserId) {
        //先删除所有的
        staffMenuService.remove(new LambdaQueryWrapper<SysStaffMenu>()
                .in(SysStaffMenu::getStaffId, staffIdList)
        );
        //再重新添加
        SysStaffMenu staffMenu = new SysStaffMenu();
        staffMenu.setCreateBy(currentUserId);
        staffIdList.forEach(staffId -> {
            staffMenu.setStaffId(staffId);
            menuId2OptionListMap.forEach((menuId, optionList) -> {
                staffMenu.setMenuId(menuId);
                List<String> list = new ArrayList<>();
                setEntity(list, optionList);
                staffMenu.setOperations(list);
                staffMenuService.save(staffMenu);
            });
        });
    }

    public void setEntity(List<String> list, List<String> optionList) {
        optionList.forEach(s -> list.add(s.split("-")[0]));
    }

    @Override
    public List<SysSetting> selectMenuHead() {
        //暂时样式和type的字段命名先固定，后期这里在维护修改
        String pcMenuAddress = MenuConstants.PC_MENU_ADDRESS;
        String pcHeaderType = MenuConstants.PC_HEADER_TYPE;
        String pcCssLogin = MenuConstants.PC_CSS_LOGIN;
        String pcOrgSetting = MenuConstants.PC_ORG_SETTING;
        return sysSettingMapper.selectMenuHead(pcMenuAddress, pcHeaderType, pcCssLogin, pcOrgSetting);
    }

    @Override
    public void saveSetting(String navValue, String cssValue, String menuAddress) {
        if (StringUtils.isEmpty(cssValue)) {
            throw new ServiceException("css样式不能为空");
        }
        Long tenantId = AuthUtils.getCurrentUserTenant();
        if (DbConstants.DEFAULT_NO.equals(tenantId)) {
            //修改：PC端导航样式  目前平台级修改
            sysSettingMapper.updateSetting(MenuConstants.PC_HEADER_TYPE, 0, navValue);
            //修改：菜单样式css  目前平台级修改
            sysSettingMapper.updateSetting(MenuConstants.PC_CSS_LOGIN, 0, cssValue);
            //修改：第三方菜单地址  目前平台级修改
            sysSettingMapper.updateSetting(MenuConstants.PC_MENU_ADDRESS, 0, menuAddress);
        }
    }

    @Override
    public BaseTreeVO listTenant(Long tenantId) {

        List<SysTenantMenu> tenantMenuList = tenantMenuService.list(new LambdaQueryWrapper<SysTenantMenu>()
                .eq(SysTenantMenu::getTenantId, tenantId)
        );
        if (ObjectUtil.isEmpty(tenantMenuList)) {
            tenantMenuList = new ArrayList<>();
        }
        //一个菜单id，对应多个操作权限，而且一个租户对应了多个菜单
        Map<Long, List<String>> menuId2HadOperationMap
                = tenantMenuList.stream()
                .collect(Collectors.toMap(SysTenantMenu::getMenuId, SysTenantMenu::getOperations));

        return listSignHad(menuId2HadOperationMap, null, null, tenantId);
    }

    @Override
    public BaseTreeVO listRole(Long roleId) {
        List<SysRoleMenu> tenantMenuList = roleMenuService.list(new LambdaQueryWrapper<SysRoleMenu>()
                .eq(SysRoleMenu::getRoleId, roleId)
        );
        if (ObjectUtil.isEmpty(tenantMenuList)) {
            tenantMenuList = new ArrayList<>();
        }
        Map<Long, List<String>> menuId2HadOperationMap
                = tenantMenuList.stream()
                .collect(Collectors.toMap(SysRoleMenu::getMenuId, SysRoleMenu::getOperations));

        return listSignHad(menuId2HadOperationMap, roleId, null, null);
    }

    /**
     * @param staffId 员工id
     *                1、通过员工id查询员工菜单；（员工菜单是在给员工添加权限时添加）
     *                2、将员工菜单集合组合成以菜单id为key，权限名为value的map（key:菜单id，value:菜单权限）
     *                3、通过该map去菜单表一一对应，将有的权限加上had字段。
     */
    @Override
    public BaseTreeVO listStaff(Long staffId) {
        //1.根据员工获取员工授权菜单
        List<SysStaffMenu> tenantMenuList = staffMenuService.list(new LambdaQueryWrapper<SysStaffMenu>()
                .eq(SysStaffMenu::getStaffId, staffId)
        );
        if (ObjectUtil.isEmpty(tenantMenuList)) {
            tenantMenuList = new ArrayList<>();
        }
        Map<Long, List<String>> menuId2HadOperationMap
                = tenantMenuList.stream()
                .collect(Collectors.toMap(SysStaffMenu::getMenuId, SysStaffMenu::getOperations));

        //2.如果是后台系统的员工，和staff菜单权限取交集过滤菜单权限
        //一个员工可以对应多个角色
        if (AuthUtils.getCurrentUserTenant() == 0) {
            return ruteRoleMenu(staffId, menuId2HadOperationMap);
        }

        return listSignHad(menuId2HadOperationMap, null, staffId, null);
    }

    public BaseTreeVO ruteRoleMenu(Long staffId, Map<Long, List<String>> menuId2HadOperationMap) {
        List<SysStaffRole> sysStaffRoles = staffRoleService.list(new LambdaQueryWrapper<SysStaffRole>()
                .eq(SysStaffRole::getStaffId, staffId));
        if (sysStaffRoles.size() == 0) {
            throw new ServiceException("该账户未绑定菜单，请联系管理员进行添加对应菜单");
        }
        List<SysRoleMenu> roleMenus = new ArrayList<>();
        sysStaffRoles.forEach(staffRole -> {
            Long roleId = staffRole.getRoleId();
            List<SysRoleMenu> sysRoleMenus = roleMenuService.list(new LambdaQueryWrapper<SysRoleMenu>()
                    .eq(SysRoleMenu::getRoleId, roleId));
            roleMenus.addAll(sysRoleMenus);
        });
        // 利用list中的元素创建HashSet集合，此时set中进行了去重操作
        HashSet<SysRoleMenu> set = new HashSet<>(roleMenus);
        // 清空list集合
        roleMenus.clear();
        // 将去重后的元素重新添加到list中
        roleMenus.addAll(set);
        Map<Long, List<String>> integerListMap = roleMenus.stream()
                .collect(Collectors.toMap(SysRoleMenu::getMenuId, SysRoleMenu::getOperations));

        Map<Long, List<String>> operationMap = new HashMap<>();
        for (Map.Entry<Long, List<String>> m : integerListMap.entrySet()) {
            List<String> roleMenuList = m.getValue();
            List<String> staffMenuList = menuId2HadOperationMap.get(m.getKey());
            List<String> list = CollectionUtils.receiveCollectionList(roleMenuList, staffMenuList);
            operationMap.put(m.getKey(), list);
        }
        return listSignHad(operationMap, null, staffId, null);
    }

    @Override
    public void addTenant(SysTenantMenu tenantMenu) {
        tenantMenuService.save(tenantMenu);
    }

    @Override
    public List<SysMenu> menuSysList(Integer sysMenu) {
        return sysMenuMapper.selectList(new LambdaQueryWrapper<SysMenu>()
                .eq(SysMenu::getSysMenu, sysMenu)
                .select());
    }

    @Override
    public void updateEntityById(SysMenu entity) {
        entity.setUpdateBy(AuthUtils.getCurrentUserId());
        entity.setUpdateTime(new Date());
        updateById(entity);
    }

    @Override
    public List<SysTenantMenu> getSysMenuById(Long tenantId) {
        return sysTenantMenuMapper.selectList(new LambdaQueryWrapper<SysTenantMenu>()
                .eq(SysTenantMenu::getTenantId, tenantId)
                .select());
    }


    /**
     * 获取菜单列表并标记权限是否拥有（人员和角色统一接口）
     * 执行逻辑：（参数包含角色ID和员工ID和租户ID，这三个参数的存在主要是为了区分两套菜单系统，
     * 如果该用户或者该角色对应的是蓝色菜单，那么就返回蓝色菜单系统，反之亦然）
     * （由于这个是统一的接口，那么就会有对应的两个方法roleId和staffId对应菜单系统）
     * 1.通过角色ID或者账号ID查出对应的角色表对象（这里可以查出对应的租户ID是多少）
     * 2.判断返回哪一套菜单系统（判断租户为0是系统菜单，角色模板也是系统菜单）
     * 3.遍历添加，并用“-”去拼接菜单id和菜单名（这个操作是为了给前端做唯一区分的作用）
     *
     * @param menuId2HadOperationsMap 拥有的菜单id：操作权限 map
     * @return 标记权限是否拥有的菜单列表
     */
    private BaseTreeVO listSignHad(Map<Long, List<String>> menuId2HadOperationsMap, Long roleId, Long staffId, Long tenantId) {
        //通过roleId和staffId获取菜单menuList集合，默认是非模板角色roleMode = 0
        Integer roleMode = 0;
        List<SysMenu> menuList;
        //判断角色
        if (ObjectUtils.isNotEmpty(roleId)) {
            SysRole sysRole = roleMapper.selectOne(new LambdaQueryWrapper<SysRole>()
                    .eq(SysRole::getId, roleId));
            //查询有效菜单集合
            if (ObjectUtils.isNotEmpty(sysRole)) {
                tenantId = sysRole.getTenantId();
                if (isEnabledRole(sysRole.getTenantId(), sysRole.getRoleMode())) {
                    roleMode = sysRole.getRoleMode();
                }
            }
        }
        //判断账号staff
        tenantId = getInteger(staffId, tenantId);

        //判断是否使用哪套菜单
        if (isEnabledRole(tenantId, roleMode)) {
            menuList = baseMapper.selectList(new LambdaQueryWrapper<SysMenu>()
                    .eq(SysMenu::getEnabled, DbConstants.DEFAULT_YES)
                    .eq(SysMenu::getDeleted, DbConstants.DEFAULT_NO)
                    .eq(SysMenu::getSysMenu, DbConstants.DEFAULT_NO));
        } else {
            menuList = baseMapper.selectList(new LambdaQueryWrapper<SysMenu>()
                    .eq(SysMenu::getEnabled, DbConstants.DEFAULT_YES)
                    .eq(SysMenu::getDeleted, DbConstants.DEFAULT_NO)
                    .eq(SysMenu::getSysMenu, DbConstants.DEFAULT_YES));
        }
        //初始化[线程安全的]返回值list
        List<SysMenuListVO> resultList = Collections.synchronizedList(new ArrayList<>());

        //多线程并发遍历
        menuList.forEach(sysMenu -> {
            SysMenuListVO menuListVO = menuMapping.po2ListVO(sysMenu);
            MenuContentVoList menuContentVoList = new MenuContentVoList();

            sysMenu.getOperations().forEach(menuContent -> {
                MenuContentVO menuContentVO = menuMapping.contentPo2ListVO(menuContent);
                List<String> tempOperationList = menuId2HadOperationsMap.get(sysMenu.getId());
                if (ObjectUtil.isNotEmpty(tempOperationList)) {
                    if (tempOperationList.contains(menuContent.getKey())) {
                        menuContentVO.setHad(true);
                    }
                }
                menuContentVoList.add(menuContentVO);
            });
            menuListVO.setOperations(menuContentVoList);
            resultList.add(menuListVO);
        });

        resultList.forEach(s -> {
            MenuContentVoList operations = s.getOperations();
            operations.forEach(operation -> operation.setKey(operation.getKey() + "-" + s.getId()));
        });
        return TreeHandler.of(SysMenuTreeVO.class).getTree(resultList);
    }

    private Long getInteger(Long staffId, Long tenantId) {
        //判断人员账号
        if (ObjectUtils.isNotEmpty(staffId)) {
            SysStaff sysStaff = staffMapper.selectOne(new LambdaQueryWrapper<SysStaff>()
                    .eq(SysStaff::getId, staffId));
            //查询有效菜单集合
            if (ObjectUtils.isNotEmpty(sysStaff)) {
                if (sysStaff.getTenantId() == 0) {
                    tenantId = sysStaff.getTenantId();
                }
            }
        }
        return tenantId;
    }

    /**
     * 判断是否是红色页面
     *
     * @param tenantId 租户id
     * @param roleMode 是否角色模板
     * @return 是否为租户为0的角色模板
     */
    private boolean isEnabledRole(Long tenantId, Integer roleMode) {
        return tenantId != 0 || roleMode.equals(DbConstants.DEFAULT_YES);
    }

    /**
     * 添加查询参数
     * <p>
     * 这个是为了适应不同实体类的树状结构条件：
     * 如果一个实体类只会有一个树状结构，则不需要添加条件，反之则需要传相关条件来确定是那颗树
     * <p>
     * 举个例子：
     * 假如我们的菜单是针对用户的，即每个用户都有一个菜单树，都保存在SysMenu表中
     * 那么我们在编辑一棵树时就需要传相关用户id来唯一确定一棵树。
     * <p>
     * 换句话说，在只有一棵树的情况下，path和parentPath都是唯一的，
     * 当存在一个或多个维度(上例中的用户)来区分树时，path和parentPath就不是唯一的了。
     * <p>
     * 所以这里需要传入对应的条件使得根据该条件能够查询到唯一的数据。
     * 按上述例子来说，就应该传入Arrays.asList(SysMenu::getUserId);
     * <p>
     * 当然这个方法也可以像其他方法一样设置成default方法，
     * 之所以不这么做是为了防止其他同事使用时会忘记重写该方法，
     * 所以宁可每次重写返回null，也不愿出现由于忘记而出现BUG。
     *
     * @return 用于Wrapper中的参数方法集合
     */
    @Override
    public List<SFunction<SysMenu, ?>> appendSelectConditionFunctions() {
        return null;
    }
}
